/*
 * Copyright (c) 2003 EISLAB, Lulea University of Technology.
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwBT Bluetooth stack.
 * 
 * Author: Conny Ohult <conny@sm.luth.se>
 *
 */
 
 /*-----------------------------------------------------------------------------------*/
 /* taken from bt_ip_dt.c
 *
 * This is a control application that initialises a host controller and connects to a 
 * network as a DT through a DUN or LAP enabled device.
 *
 *  Modified for for basic rfcomm connection???
 */
/*-----------------------------------------------------------------------------------*/

#define BT_IP_DEBUG DBG_ON /* Control debug messages */

struct rfcomm_pcb *global_pcb;

int fifo_fd_Rx;
int fifo_fd_Tx;

enum bt_profile {
  LAP_PROFILE, DUN_PROFILE
};

struct bt_state {
  enum bt_profile profile;
  struct tcp_pcb *tcppcb;
  struct bd_addr bdaddr;
  struct pbuf *p;
  u8_t btctrl;
  u8_t cn;
} bt_ip_state;

/* Forward declarations */
err_t rfcomm_connected(void *arg, struct rfcomm_pcb *tpcb, err_t err);
err_t l2cap_connected(void *arg, struct l2cap_pcb *pcb, u16_t result, u16_t status);
err_t inquiry_complete(void *arg, struct hci_pcb *pcb, struct hci_inq_res *ires, u16_t result);
err_t command_complete(void *arg, struct hci_pcb *pcb, u8_t ogf, u8_t ocf, u8_t result);
err_t pin_req(void *arg, struct bd_addr *bdaddr);
err_t our_l2ca_pong(void *arg, struct l2cap_pcb *pcb, u8_t result);

err_t pin_reply(u8_t *pin_code, u8_t pinlen);

err_t our_l2ca_connect_ind(void *arg, struct l2cap_pcb *pcb, err_t err);

err_t our_rfcomm_input(void *arg, struct rfcomm_pcb *pcb, struct pbuf *p, err_t err);
err_t obex_rfcomm_input(void *arg, struct rfcomm_pcb *pcb, struct pbuf *p, err_t err);

err_t fifo_server_setup(void);
err_t fifo_server_loop(void);


static const u8_t lap_service_record[] = {
  0x35, 0x8, 
  0x9, 0x0, 0x0, 0xa, 0x0, 0x0, 0xff, 0xff, /* Service record handle attribute */
  0x35, 0x8, 
  0x9, 0x0, 0x1, 0x35, 0x3, 0x19, 0x11, 0x2, /* Service class ID list attribute */
  0x35, 0x11,
  0x9, 0x0, 0x4, 0x35, 0xc, 0x35, 0x3, 0x19, 0x1, 0x0, 0x35, 0x5, 0x19, 0x0, 0x3, 0x8, 0x1 /* Protocol descriptor list attribute */
};

u8_t bt_ip_netifn;

/*-----------------------------------------------------------------------------------*/
/* 
* bt_rfcomm_start():
*
* Called by the main application to initialize and connect to a network
*
*/
/*-----------------------------------------------------------------------------------*/
void
bt_rfcomm_start(void)
{
  hci_reset_all();
  l2cap_reset_all();
  sdp_reset_all();
  rfcomm_reset_all();

#ifdef DEBUG_HIGH
  printf("Starting bt_rfcomm... (or restarting)\n");
#endif

  hci_cmd_complete(command_complete);
  hci_pin_req(pin_req);

  bt_ip_state.btctrl = 0;
  bt_ip_state.p = NULL;
  bt_ip_state.tcppcb = NULL;
  bt_ip_netifn = 0;

  hci_reset();
}

/*-----------------------------------------------------------------------------------*/
/* 
* rfcomm_disconnected():
*
* Called by RFCOMM when the remote RFCOMM protocol or upper layer was disconnected.
* Disconnects the PPP protocol.
*
*/
/*-----------------------------------------------------------------------------------*/
err_t
rfcomm_disconnected(void *arg, struct rfcomm_pcb *pcb, err_t err) 
{
  err_t ret = ERR_OK;

  LWIP_DEBUGF(BT_IP_DEBUG, ("rfcomm_disconnected: CN = %d\n", rfcomm_cn(pcb)));
  if(rfcomm_cn(pcb) != 0) {
    //ret = ppp_lp_disconnected(pcb);
    ret = 0;
  }
  rfcomm_close(pcb);
  
  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
* l2cap_disconnected_ind():
*
* Called by L2CAP to indicate that remote L2CAP protocol disconnected.
* Disconnects the RFCOMM protocol and the ACL link before it initializes a search for 
* other devices.
*
*/
/*-----------------------------------------------------------------------------------*/
err_t
l2cap_disconnected_ind(void *arg, struct l2cap_pcb *pcb, err_t err)
{
  err_t ret = ERR_OK;

  LWIP_DEBUGF(BT_IP_DEBUG, ("l2cap_disconnected_ind: L2CAP disconnected\n"));

  if(pcb->psm == SDP_PSM) { 
    sdp_lp_disconnected(pcb);
    l2cap_close(pcb);
  } else if(pcb->psm == RFCOMM_PSM) {
    ret = rfcomm_lp_disconnected(pcb);
    /* We can do this since we know that we are the only channel on the ACL link. If ACL link already is 
     down we get an ERR_CONN returned */
    hci_disconnect(&(pcb->remote_bdaddr), HCI_OTHER_END_TERMINATED_CONN_USER_ENDED);
    l2cap_close(pcb);
    bt_rfcomm_start();
  }
  
  return ret;
}

/*-----------------------------------------------------------------------------------*/
// Global definitaion 
struct bd_addr *in_progress_bdaddr;

/*-----------------------------------------------------------------------------------*/
/*
 * pin_reply():
 *
 * Continue w/ rfcomm connect process with specified pin
 */
/*-----------------------------------------------------------------------------------*/
err_t
pin_reply(u8_t *pin_code, u8_t pinlen) {

	//printf("global bd_addr = %02X:%02X:%02X:%02X:%02X:%02X\n", in_progress_bdaddr[0].addr[5], in_progress_bdaddr[0].addr[4], in_progress_bdaddr[0].addr[3], in_progress_bdaddr[0].addr[2], in_progress_bdaddr[0].addr[1], in_progress_bdaddr[0].addr[0]);
	//printf("  pin_code = %s   pinlen = %d \n", pin_code, pinlen);

	err_t return_code;
	return_code = hci_pin_code_request_reply(in_progress_bdaddr, pinlen, ((u8_t *) pin_code));
	return 0;
}
/*-----------------------------------------------------------------------------------*/
/*
 * pin_req():
 *
 * Called by HCI when a request for a PIN code has been received. A PIN code is 
 * required to create a new link key.
 * Replys to the request with the given PIN code
 *
 */
/*-----------------------------------------------------------------------------------*/
err_t 
pin_req(void *arg, struct bd_addr *bdaddr)
{
  LWIP_DEBUGF(BT_IP_DEBUG, ("pin_req\n"));
  byte pin_code[10];
  //sprintf((char *) pin_code, "1234");
  sprintf((char *) pin_code, "0000");
#ifdef DEBUG_MED
  printf("    using pin_code = %s \n", pin_code);
#endif
  in_progress_bdaddr = bdaddr;

  //pin_reply((u8_t *) pin_code, 4);
  //return hci_pin_code_request_reply(bdaddr, 4, ((u8_t *) "1234"));
  if (lib_interactive_pin == 0) {
    return hci_pin_code_request_reply(bdaddr, 4, ((u8_t *) pin_code));
  } else {
    // clear lib_is_scanning flag to (temporarily) stop rfcomm connection
    // but respond w/ pin code request by calling pin_reply(), see above)
    lib_is_connecting = 0;
    rfcomm_connection_state = 10; 
    return ERR_OK;
  }
}
/*-----------------------------------------------------------------------------------*/
/*
* link_key_not():
 *
 * Called by HCI when a new link key has been created for the connection
 * Writes the key to the Bluetooth host controller, where it can be stored for future
 * connection attempts.
  *
*/
/*-----------------------------------------------------------------------------------*/
err_t 
link_key_not(void *arg, struct bd_addr *bdaddr, u8_t *key)
{
  LWIP_DEBUGF(BT_IP_DEBUG, ("link_key_not\n"));
#ifdef DEBUG_MED
  printf("in link key notification...\n");
#endif
  return hci_write_stored_link_key(bdaddr, key); /* Write link key to be stored in the
                                                    Bluetooth host controller */
}
/*-----------------------------------------------------------------------------------*/
/*
 * l2cap_disconnected_cfm():
 *
 * Called by L2CAP to confirm that the L2CAP disconnection request was successful
 *
 */
/*-----------------------------------------------------------------------------------*/
err_t
l2cap_disconnected_cfm(void *arg, struct l2cap_pcb *pcb) 
{
  LWIP_DEBUGF(BT_IP_DEBUG, ("l2cap_disconnected_cfm\n"));
#ifdef DEBUG_MED
  printf("inside l2cap_disconnected_cfm ...\n");
#endif
  l2cap_close(pcb);
  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
/*
 * get_rfcomm_cn():
 *
 * Parse the RFCOMM channel number from an SDP attribute list
 *
*/
/*-----------------------------------------------------------------------------------*/
u8_t
get_rfcomm_cn(u16_t attribl_bc, struct pbuf *attribute_list)
{
  printf("inside get_rfcomm_cn (attribl_bc = %d) \n", attribl_bc); fflush(stdout);
  printf("SDP_DE_TYPE_UUID | SDP_DE_SIZE_16 = 0x%02X \n", (SDP_DE_TYPE_UUID | SDP_DE_SIZE_16));
  u16_t i;
  for(i = 0; i < attribl_bc; i++) {
#ifdef DEBUG_MED
if (1) {
//  printf("inside for loop. (i=%d) ", i); fflush(stdout);
  if ((i>00) & (i<400)) {
    u8_t temp;
    temp = ((u8_t *)attribute_list->payload)[i];
    if (temp != 0) {printf("payload)[%d] = 0x%02X \n", i, ((u8_t *)attribute_list->payload)[i]);}
  }
}
#endif

    if(((u8_t *)attribute_list->payload)[i] == (SDP_DE_TYPE_UUID | SDP_DE_SIZE_16)) {
#ifdef DEBUG_LOW
printf("  found uuid, checking attribute ... \n");
#endif
      if(ntohs(*((u16_t *)(((u8_t *)attribute_list->payload)+i+1))) == 0x0003) {
#ifdef DEBUG_LOW
printf("    found correct attribute...\n"); fflush(stdout);
#endif
	return *(((u8_t *)attribute_list->payload)+i+4);
      }
    }
  }
#ifdef DEBUG_MED
  printf("get_rfcomm_cn returning 0 \n"); fflush(stdout);
#endif
  return 0;
}
/*-----------------------------------------------------------------------------------*/
/*
 * rfcomm_connected():
 *
 * Called by RFCOMM when a connection attempt response was received.
 * Creates a RFCOMM connection for the channel retreived from SDP.
 * Initializes a search for other devices if the connection attempt failed.
 */
/*-----------------------------------------------------------------------------------*/
err_t
rfcomm_connected(void *arg, struct rfcomm_pcb *pcb, err_t err) 
{
  struct pbuf *p;
//  struct ip_addr ipaddr, netmask, gw;
//  struct netif *netif;
//  struct ppp_pcb *ppppcb;

  //printf("\n\ninside rfcomm_connected. CN = %d\n", rfcomm_cn(pcb));

  if(err == ERR_OK) {
    LWIP_DEBUGF(BT_IP_DEBUG, ("rfcomm_connected. CN = %d\n", rfcomm_cn(pcb)));
    printf("\nSuccess, rfcomm_connected to channel:  %d\n", rfcomm_cn(pcb));
    lib_is_connecting = 0;
    rfcomm_connection_state = RFCOMM_CONNECTED;
 
    rfcomm_disc(pcb, rfcomm_disconnected);

    if(bt_ip_state.profile == DUN_PROFILE) {
      printf("\n\nDUN profile, this one's wrong...\n");
      /* Establish a GPRS connection */
//      LWIP_DEBUGF(BT_IP_DEBUG, ("rfcomm_msc_rsp: Establish a GPRS connection\n"));
//      rfcomm_recv(pcb, at_input);
      //p = pbuf_alloc(PBUF_RAW, sizeof("ATZ\r")-1, PBUF_RAM);
      //((u8_t *)p->payload) = "ATZ\r";
//      p = pbuf_alloc(PBUF_RAW, sizeof("AT\r"), PBUF_RAM);
//      ((u8_t *)p->payload) = "AT\r";
//      at_state = 0;
      if(rfcomm_cl(pcb)) {
	rfcomm_uih_credits(pcb, 6,  p);
      } else {
	rfcomm_uih(pcb, rfcomm_cn(pcb), p);
      }
      pbuf_free(p);
    } else {
      /* Establish a RFCOMM connection (via /de/rfocmm???)  */
//      printf("\n\n   TODO: Need to still establish an\n   rfcomm i/o link and make sure \n");
//      printf    ("   something gets assigned to \n   rfcomm_recv; do this now!!!!\n\n\n");

      rfcomm_recv(pcb, our_rfcomm_input);
      //rfcomm_recv(pcb, obex_rfcomm_input);

      global_pcb = pcb;

//      /* Establish a PPP connection */
//      if((ppppcb = ppp_new(pcb)) == NULL) {
//	LWIP_DEBUGF(BT_IP_DEBUG, ("rfcomm_msc_rsp: Could not alloc PPP pcb\n"));
//	return ERR_MEM;
//      }
      
      /* Add PPP network interface to lwIP */
//      gw.addr = 0;
//      ipaddr.addr = 0;
//      IP4_ADDR(&netmask, 255,255,255,0);
      
//      netif = netif_add(&ipaddr, &netmask, &gw, NULL, bluetoothif_init, ip_input);
      
//      netif_set_default(netif);
      
//      ppp_netif(ppppcb, netif);
 
// TODO: rfcomm_recv nees to get mapped to something routed to /dev/rfcommXX     
//      rfcomm_recv(pcb, ppp_input);

//      ppp_disconnected(ppppcb, ppp_is_disconnected);
//      return ppp_connect(ppppcb, ppp_connected); 
        return 0;
    }
  } else {
    LWIP_DEBUGF(BT_IP_DEBUG, ("rfcomm_connected. Connection attempt failed CN = %d\n", rfcomm_cn(pcb)));
    l2cap_close(pcb->l2cappcb);
    rfcomm_close(pcb);
    bt_rfcomm_start();
  }
  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
/*
 * sdp_attributes_recv():
 *
 * Can be used as a callback by SDP when a response to a service attribute request or 
 * a service search attribute request was received.
 * Disconnects the L2CAP SDP channel and connects to the RFCOMM one.
 * If no RFCOMM channel was found it initializes a search for other devices.
 */
/*-----------------------------------------------------------------------------------*/
void
sdp_attributes_recv(void *arg, struct sdp_pcb *sdppcb, u16_t attribl_bc, struct pbuf *p)
{
  struct l2cap_pcb *l2cappcb;

  //printf("\n\n holy crap, ive received an sdp request..... \n\n");
#ifdef DEBUG_HIGH
  printf("\n\n   Received viable sdp request, now to disconnect\n");
  printf("   and find channel to start rfcomm... \n\n");
#endif

  l2ca_disconnect_req(sdppcb->l2cappcb, l2cap_disconnected_cfm);

  /* Get the RFCOMM channel identifier from the protocol descriptor list */
  if((bt_ip_state.cn = get_rfcomm_cn(attribl_bc, p)) != 0) {
//printf("inside if...\n"); fflush(stdout);
    if((l2cappcb = l2cap_new()) == NULL) {
      LWIP_DEBUGF(BT_IP_DEBUG, ("sdp_attributes_recv: Could not alloc L2CAP pcb\n"));
      return;
    }
    LWIP_DEBUGF(BT_IP_DEBUG, ("sdp_attributes_recv: RFCOMM channel: %d\n", bt_ip_state.cn));
#ifdef DEBUG_LOW
    printf("sdp_attributes_recv: RFCOMM channel: %d\n", bt_ip_state.cn);
#endif
    if(bt_ip_state.profile == DUN_PROFILE) {
#ifdef DEBUG_HIGH
      printf("making actual rfcomm connection (DUN profile?()...\n");
#endif
      l2ca_connect_req(l2cappcb, &(sdppcb->l2cappcb->remote_bdaddr), RFCOMM_PSM, 0, l2cap_connected);
    } else {
#ifdef DEBUG_HIGH
      printf("making RFCOMM_PSM connection ...\n");
#endif
      //l2cap_connect_ind(l2cappcb, SDP_PSM, our_l2ca_connect_ind);
      l2ca_connect_req(l2cappcb, &(sdppcb->l2cappcb->remote_bdaddr), RFCOMM_PSM, HCI_ALLOW_ROLE_SWITCH, l2cap_connected);
      //l2ca_connect_req(l2cappcb, &(sdppcb->l2cappcb->remote_bdaddr), RFCOMM_PSM, 0, l2cap_connected);
    }
    
  } else {
#ifdef DEBUG_MED
    printf("connection failed restarting bt_rfcomm ...\n\n"); fflush(stdout);
#endif
    lib_is_connecting = 0;
    rfcomm_connection_state = RFCOMM_NO_CHAN; 
    bt_rfcomm_start();
  }
  sdp_free(sdppcb);
}
/*-----------------------------------------------------------------------------------*/
/*
 * l2cap_connected():
 *
 * Called by L2CAP when a connection response was received.
 * Sends a L2CAP configuration request.
 * Initializes a search for other devices if the connection attempt failed.
 */
/*-----------------------------------------------------------------------------------*/
err_t
l2cap_connected(void *arg, struct l2cap_pcb *l2cappcb, u16_t result, u16_t status)
{
  struct sdp_pcb *sdppcb;
  struct rfcomm_pcb *rfcommpcb;

#ifdef DEBUG_MED
  printf("\ninside l2cap_connected!!! \n\n");
#endif

  //u8_t ssp[] = {0x35, 0x03, 0x19, 0x11, 0x02}; /* Service search pattern with LAP UUID is default */ 
 // u8_t ssp[] = {0x35, 0x03, 0x19, 0x11, 0x01}; /* Service search pattern with SPP UUID is default */ 
  //u8_t ssp[] = {0x35, 0x03, 0x19, 0x10, 0x02}; /* Service search pattern with PublicBrowse UUID is default */ 
  
  /* note: when establishing rfcomm connections, iPhone will now search for OBEX Push services enabled */
  
  //u8_t ssp[] = {0x35, 0x03, 0x19, 0x11, 0x05}; /* Service search pattern with OBEX Push UUID is default */
  u8_t ssp[] = {0x35, 0x03, 0x19, 0x11, 0x24}; /* Service search pattern with HID UUID is default */

  err_t ret;

  u8_t attrids[] = {0x35, 0x03, 0x09, 0x00, 0x04}; /* Attribute IDs to search for in data element
						      sequence form */
//  u8_t attrids[] = {0x35, 0x05, 0x0A, 0x00, 0x00, 0xFF, 0xFF}; /* Attribute IDs to search for in data element 
//						      sequence form */

  if(result == L2CAP_CONN_SUCCESS) {
    LWIP_DEBUGF(BT_IP_DEBUG, ("l2cap_connected: L2CAP connected pcb->state = %d\n", l2cappcb->state));
    /* Tell L2CAP that we wish to be informed of a disconnection request */
    l2cap_disconnect_ind(l2cappcb, l2cap_disconnected_ind);
    switch(l2cap_psm(l2cappcb)) {
    case SDP_PSM:
      LWIP_DEBUGF(BT_IP_DEBUG, ("l2cap_connected: SDP L2CAP configured. Result = %d\n", result));
      //printf(" \n\n hey there, inside SDP_PSM case!!! \n");
      if(bt_ip_state.profile == DUN_PROFILE) {
	LWIP_DEBUGF(BT_IP_DEBUG, ("l2cap_connected: Using DUN profile\n"));
	ssp[4] = 0x03; /* Change service search pattern to contain DUN UUID */
	ssp[4] = 0x01; /* Change service search pattern to contain SP UUID */
      } else {
	LWIP_DEBUGF(BT_IP_DEBUG, ("l2cap_connected: Using LAP profile\n"));
      }
      
      if((sdppcb = sdp_new(l2cappcb)) == NULL) {
	LWIP_DEBUGF(BT_IP_DEBUG, ("l2cap_connected: Failed to create a SDP PCB\n"));
	printf("l2cap_connected: Failed to create a SDP PCB\n");
	return ERR_MEM;
      }
      
      l2cap_recv(l2cappcb, sdp_recv);
     

      u16_t max; 
      max = 0xFFFF;
      ret = sdp_service_search_attrib_req(sdppcb, 0xFFFF, ssp, sizeof(ssp), attrids, sizeof(attrids),
					  sdp_attributes_recv);
//      ret = sdp_service_search_attrib_req(sdppcb, max, ssp, sizeof(ssp), attrids, sizeof(attrids),
//					  sdp_attributes_recv);
      return ret;
    case RFCOMM_PSM:
      LWIP_DEBUGF(BT_IP_DEBUG, ("l2cap_connected: RFCOMM L2CAP configured. Result = %d CN = %d\n", result, bt_ip_state.cn));
      //printf(" \n\n hey there, inside RFCOMM_PSM case!!! \n");
      l2cap_recv(l2cappcb, rfcomm_input);

      if((rfcommpcb = rfcomm_new(l2cappcb)) == NULL) {
	LWIP_DEBUGF(BT_IP_DEBUG, ("l2cap_connected: Failed to create a RFCOMM PCB\n"));
	printf("l2cap_connected: Failed to create a RFCOMM PCB\n");
	return ERR_MEM;
      }

      hci_link_key_not(link_key_not); /* Set function to be called if a new link key is created */


     
      err_t temp;
      temp = 0;
      temp = rfcomm_connect(rfcommpcb, bt_ip_state.cn, rfcomm_connected); /* Connect with DLCI 0 */
      //temp = rfcomm_connect(rfcommpcb, 1, rfcomm_connected); /* Connect with DLCI 0 */
      //printf("temp = %d \n", temp);
      return temp;
      //return rfcomm_connect(rfcommpcb, bt_ip_state.cn, rfcomm_connected); /* Connect with DLCI 0 */
    default:
      return ERR_VAL;
    }
  } else {
    LWIP_DEBUGF(BT_IP_DEBUG, ("l2cap_connected: L2CAP not connected. Redo inquiry\n"));
    l2cap_close(l2cappcb);
    
    //bt_rfcomm_start();
 
    // clear the is_connecting flag and set the state
    lib_is_connecting = 0;
    rfcomm_connection_state = RFCOMM_NO_DEV;
 
  }
  
  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
/*
 * inquiry_complete():
 *
 * Called by HCI when a inquiry complete event was received.
 * Connects to the first device in the list.
 * Initializes a search for other devices if the inquiry failed.
 */
/*-----------------------------------------------------------------------------------*/
err_t
inquiry_complete(void *arg, struct hci_pcb *pcb, struct hci_inq_res *ires, u16_t result)
{
  struct l2cap_pcb *l2cappcb;

#ifdef DEBUG_MED
  printf("\ninside inquiry complete...\n\n");
#endif
  if(result == HCI_SUCCESS) {
#ifdef DEBUG_LOW
    printf("\n  HCI_SUCCESS ...\n");
#endif
    LWIP_DEBUGF(BT_IP_DEBUG, ("successful Inquiry\n"));
    if(ires != NULL) {
      LWIP_DEBUGF(BT_IP_DEBUG, ("Initiate L2CAP connection\n"));
#ifdef DEBUG_LOW
      //printf("Initiate L2CAP connection (?)\n");
      LWIP_DEBUGF(BT_IP_DEBUG, ("ires->psrm %d\n ires->psm %d\n ires->co %d\n", ires->psrm, ires->psm, ires->co));
      LWIP_DEBUGF(BT_IP_DEBUG, ("ires->bdaddr 0x%x:0x%x:0x%x:0x%x:0x%x:0x%x\n", ires->bdaddr.addr[5], ires->bdaddr.addr[4], ires->bdaddr.addr[3], ires->bdaddr.addr[2], ires->bdaddr.addr[1], ires->bdaddr.addr[0]));
#endif

//      printf("ires->cod[1] & 0x1F = %d \n", (ires->cod[1] & 0x1F));
      if((ires->cod[1] & 0x1F) == 0x03) {
	bt_ip_state.profile = LAP_PROFILE;
      } else {
	bt_ip_state.profile = DUN_PROFILE;
	bt_ip_state.profile = LAP_PROFILE;	// always set to LAP PROFILE
      }
      
      if((l2cappcb = l2cap_new()) == NULL) {
	LWIP_DEBUGF(BT_IP_DEBUG, ("inquiry_complete: Could not alloc L2CAP pcb\n"));
	printf(" -----> inquiry_complete: Could not alloc L2CAP pcb\n");
	return ERR_MEM;
      } 

#ifdef DEBUG_LOW
//      u8_t count;
//      printf("ires->bdaddr = ");
//      for (count = 0; count < 6; count++) {
//          printf("%02X", ires->bdaddr.addr[5-count]);
//          if (count < 5) {printf(":");}
//      }
//      printf("\n");
#endif


      if(bt_ip_state.profile == DUN_PROFILE) {
#ifdef DEBUG_LOW
	//printf(" (wanting to) Trying to make rfcomm connection (for DUN) ???\n");
#endif
        //l2ca_connect_req(l2cappcb, &(ires->bdaddr), SDP_PSM, 0, l2cap_connected);
	//l2ca_connect_req(l2cappcb, &(ires->bdaddr), SDP_PSM, HCI_ALLOW_ROLE_SWITCH, l2cap_connected);
      } else {
	//printf(" Trying to make rfcomm connection (for ?) ?\n");
	//l2ca_connect_req(l2cappcb, &(ires->bdaddr), SDP_PSM, HCI_ALLOW_ROLE_SWITCH, l2cap_connected);
      }
    } else {
#ifdef DEBUG_LOW
      printf("ires = NULL ?????\n");
#endif
      //hci_inquiry2(0x009E8B33, 0x04, 0x02, inquiry_complete);
    }
  } else {
    LWIP_DEBUGF(BT_IP_DEBUG, ("Unsuccessful Inquiry.\n"));
    printf("Unsuccessful Inquiry.\n");
    //hci_inquiry2(0x009E8B33, 0x04, 0x01, inquiry_complete);
  }
 
  // clear the lib_is_scanning flag to let library know hci scan is complete 

  lib_is_scanning=0;
  if (lib_threaded == 1) {
    if (lib_callbacks->scan_complete != NULL) {
      lib_callbacks->scan_complete();
    }
  }

  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
/*
 * acl_wpl_complete():
 *
 * Called by HCI when a successful write link policy settings complete event was
 * received.
 */
/*-----------------------------------------------------------------------------------*/
err_t
acl_wpl_complete(void *arg, struct bd_addr *bdaddr)
{
  hci_sniff_mode(bdaddr, 200, 100, 10, 10);
  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
/*
 * acl_conn_complete():
 *
 * Called by HCI when a connection complete event was received.
 */
/*-----------------------------------------------------------------------------------*/
err_t
acl_conn_complete(void *arg, struct bd_addr *bdaddr)
{
  //hci_wlp_complete(acl_wpl_complete);
  //hci_write_link_policy_settings(bdaddr, 0x000F);
  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
/*
 * read_bdaddr_complete():
 *
 * Called by HCI when a read local bluetooth device address complete event was received.
 */
/*-----------------------------------------------------------------------------------*/
err_t
read_bdaddr_complete(void *arg, struct bd_addr *bdaddr)
{
  memcpy(&(bt_ip_state.bdaddr), bdaddr, 6);
  
  int i;
#ifdef DEBUG_HIGH
  printf("\nlocal BD_ADDR = ");
#endif
  for (i=0; i<6; i++) {
    local_mac_addr[i] = bdaddr[0].addr[5-i];
#ifdef DEBUG_HIGH
    printf("%02X", bdaddr[0].addr[5-i]);
    if (i<5) {printf(":");}
#endif
  }
#ifdef DEBUG_HIGH
  printf("\n\n");
#endif

  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
/*
 * command_complete():
 *
 * Called by HCI when an issued command has completed during the initialization of the
 * host controller.
 * Initializes a search for other devices when host controller initialization is
 * completed.
 */
/*-----------------------------------------------------------------------------------*/
err_t
command_complete(void *arg, struct hci_pcb *pcb, u8_t ogf, u8_t ocf, u8_t result)
{
  //u8_t cod_lap_dun[] = {0x00,0x02,0x00,0x00,0x1E,0x00};
    

#ifdef DEBUG_LOW
  printf("\ninside command_complete...\n\n");
#endif
  switch(ogf) {
  case HCI_INFO_PARAM:
    switch(ocf) {
    case HCI_READ_BUFFER_SIZE:
      if(result == HCI_SUCCESS) {
	LWIP_DEBUGF(BT_IP_DEBUG, ("successful HCI_READ_BUFFER_SIZE.\n"));
#ifdef DEBUG_MED
	printf("successful HCI_READ_BUFFER_SIZE.\n");
#endif
	hci_read_bd_addr(read_bdaddr_complete);
      } else {
	LWIP_DEBUGF(BT_IP_DEBUG, ("Unsuccessful HCI_READ_BUFFER_SIZE.\n"));
	printf("Unsuccessful HCI_READ_BUFFER_SIZE.\n");
	return ERR_CONN;
      }
      break;
    case HCI_READ_BD_ADDR:
      if(result == HCI_SUCCESS) {
	LWIP_DEBUGF(BT_IP_DEBUG, ("successful HCI_READ_BD_ADDR.\n"));
#ifdef DEBUG_MED
	printf("successful HCI_READ_BD_ADDR.\n");
#endif
	//hci_set_event_filter(0x01, 0x01, cod_lap_dun); /* Report only devices with a specific type of CoD */


	hci_set_event_filter(0x00, 0x00, 0); /* clear event filters (???) */
        
	
      } else {
	LWIP_DEBUGF(BT_IP_DEBUG, ("Unsuccessful HCI_READ_BD_ADDR.\n"));
	printf("Unsuccessful HCI_READ_BD_ADDR.\n");
	return ERR_CONN;
      }
      break;
    default:
      LWIP_DEBUGF(BT_IP_DEBUG, ("Unknown HCI_INFO_PARAM command complete event\n"));
      printf("Unknown HCI_INFO_PARAM command complete event\n");
      break;
    }
    break;
  case HCI_HC_BB_OGF:
    switch(ocf) {
    case HCI_RESET:
      if(result == HCI_SUCCESS) {
	LWIP_DEBUGF(BT_IP_DEBUG, ("successful HCI_RESET.\n")); 
#ifdef DEBUG_MED
	printf("successful HCI_RESET.\n"); 
#endif
	hci_read_buffer_size();
      } else {
	LWIP_DEBUGF(BT_IP_DEBUG, ("Unsuccessful HCI_RESET.\n"));
	printf("Unsuccessful HCI_RESET.\n");
	return ERR_CONN;
      }
      break;
    case HCI_SET_EVENT_FILTER:
      if(result == HCI_SUCCESS) {
	LWIP_DEBUGF(BT_IP_DEBUG, ("successful HCI_SET_EVENT_FILTER.\n"));
#ifdef DEBUG_MED
	printf("successful HCI_SET_EVENT_FILTER.\n");
#endif
	hci_write_page_timeout(0x4000); /* 10.24s */
      } else {
	LWIP_DEBUGF(BT_IP_DEBUG, ("Unsuccessful HCI_SET_EVENT_FILTER.\n"));
	printf("Unsuccessful HCI_SET_EVENT_FILTER.\n");
	return ERR_CONN;
      }
      break;
    case HCI_READ_LOC_NAME:
      if(result == HCI_SUCCESS) {
          printf(" processing local name....\n");
      }
    case HCI_WRITE_PAGE_TIMEOUT:
      if(result == HCI_SUCCESS) {
	LWIP_DEBUGF(BT_IP_DEBUG, ("successful HCI_WRITE_PAGE_TIMEOUT.\n"));
#ifdef DEBUG_MED
	printf("successful HCI_WRITE_PAGE_TIMEOUT.\n");
#endif
	hci_cmd_complete(NULL); /* Initialization done, don't come back */
	//hci_connection_complete(acl_conn_complete);
	LWIP_DEBUGF(BT_IP_DEBUG, ("Initialization done.\n"));
#ifdef DEBUG_MED
	printf("Initialization done.\n");
#endif
	LWIP_DEBUGF(BT_IP_DEBUG, ("Discover other Bluetooth devices.\n"));
	//printf("Discover other Bluetooth devices(?).\n");
	//hci_inquiry2(0x009E8B33, 0x0A, 0x08, inquiry_complete); //TODO: FAILED????
        //hci_read_local_name();
        //printf("payload in after read_local_name = %d \n", *((u16_t *)cb->p->payload));
        //phybusif_input(cb); /* Check for input ???? */
      } else {
	LWIP_DEBUGF(BT_IP_DEBUG, ("Unsuccessful HCI_WRITE_PAGE_TIMEOUT.\n"));
	printf("Unsuccessful HCI_WRITE_PAGE_TIMEOUT.\n");
	return ERR_CONN;
      }
      break;
    default:
      LWIP_DEBUGF(BT_IP_DEBUG, ("Unknown HCI_HC_BB_OGF command complete event\n"));
      printf("Unknown HCI_HC_BB_OGF command complete event\n");
      break;
    }
    break;
  default:
    LWIP_DEBUGF(BT_IP_DEBUG, ("Unknown command complete event. OGF = 0x%x OCF = 0x%x\n", ogf, ocf));
    printf("Unknown command complete event. OGF = 0x%x OCF = 0x%x\n", ogf, ocf);
    break;
  }
  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
/*
 * our_l2ca_disconnect_ind():
 *
 * Called when l2cap disonnection request was received. (modeled after bt_disconnect_ind from bt_ip_lap.c)
 */
/*-----------------------------------------------------------------------------------*/
err_t
our_l2ca_disconnect_ind(void *arg, struct l2cap_pcb *pcb, err_t err)
{
	err_t ret;

#ifdef DEBUG_LOW
	printf("inside our_l2ca_disconnect_ind\n");
#endif

	if(pcb->psm == SDP_PSM) { 
		sdp_lp_disconnected(pcb);
	} else if(pcb->psm == RFCOMM_PSM) {
		ret = rfcomm_lp_disconnected(pcb);
	}

	l2cap_close(pcb);
	
	return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
/*
 * our_l2ca_connect_ind():
 *
 * Called when l2cap connection request was received. (modeled after bt_connect_ind from bt_ip_lap.c)
 */
/*-----------------------------------------------------------------------------------*/
err_t
our_l2ca_connect_ind(void *arg, struct l2cap_pcb *pcb, err_t err)
{
#ifdef DEBUG_LOW
	printf("inside our_l2ca_connect_ind ...\n");
#endif

	/* Tell L2CAP that we wish to be informed of a disconnection request */
	l2cap_disconnect_ind(pcb, our_l2ca_disconnect_ind);

	/* Tell L2CAP that we wish to be informed of incoming data */
	if(pcb->psm == SDP_PSM) {
		l2cap_recv(pcb, sdp_recv);
	} else if (pcb->psm == RFCOMM_PSM) {
		l2cap_recv(pcb, rfcomm_input);
	}

	return ERR_OK;
}




/*-----------------------------------------------------------------------------------*/
/*
 * our_l2ca_pong():
 *
 * Called when l2cap echo request has been recied.
 */
/*-----------------------------------------------------------------------------------*/
err_t
our_l2ca_pong(void *arg, struct l2cap_pcb *pcb, u8_t result)
{

	printf("n\nreceived a l2ca_ping resposne....\n");
	return 0;
}
/*-----------------------------------------------------------------------------------*/




/*-----------------------------------------------------------------------------------*/
/*
 * our_rfcomm_input():
 *
 * Called by RFCOMM when general data has been received.
 */
/*-----------------------------------------------------------------------------------*/
err_t
our_rfcomm_input(void *arg, struct rfcomm_pcb *pcb, struct pbuf *p, err_t err) {

	// Pull data from pbuf (payload), write to /dev/rfcommX, then free the pbuf(?)
//	printf(" first element = %d \n", ((u8_t *)p->payload)[0] );

	int i;
   	//printf("p->len = %d    p->tot_len = %d \n", p->len, p->tot_len);

if (0) {
	for (i=0; i < p->len; i++) {
		printf("data[%d] = %d \n", i, ((u8_t *)p->payload)[i] );
		//write(fifo_fd_Tx, &( ((u8_t *)p->payload)[i] ), 1);
	}
}
	//write(fifo_fd_Tx, &( ((u8_t *)p->payload)[i] ), 1);
	write(fifo_fd_Tx, ((u8_t *)p->payload), p->len);

	struct pbuf *t;
	t = p;
	if (t->next != NULL) {
#ifdef DEBUG_LOW
		printf("  writing next payload to fifo (for continued segments)...\n");
#endif
		t = t->next;
		write(fifo_fd_Tx, ((u8_t *)t->payload), t->len);
	}

	// finally free up the physical buffer used for input
	if (p->next != NULL) {
		pbuf_free(p->next);
	}
	pbuf_free(p);

	return ERR_OK;
}


/*-----------------------------------------------------------------------------------*/
/*
 * our_rfcomm_output():
 *
 * Put data into rfcomm payload and send it out via unnumbered information frame.
 */
/*-----------------------------------------------------------------------------------*/
err_t
our_rfcomm_output(byte *data, int num_bytes) {

	// use the rfcomm_pcb passed into the rfcomm_connected function as the 'global' pcb 
	
	// Put data into rfcomm_pcb payload, send it out then free the buffer
	struct pbuf *p;
	p = pbuf_alloc(PBUF_RAW, num_bytes, PBUF_RAM);
	memcpy( ((u8_t *)p->payload) , data, num_bytes);

if (0) {
printf("\n sending via rfcomm... \n");
int i;
for (i=0; i< num_bytes; i++) {
	printf("data[%d] = %d \n", i, ((u8_t *)p->payload)[i] );
}
}
	if(rfcomm_cl(global_pcb)) {
		// why 6 credits??
		//printf("using uih_credits = 6 \n");
		rfcomm_uih_credits(global_pcb, 6,  p);
	} else {
		rfcomm_uih(global_pcb, rfcomm_cn(global_pcb), p);
	}

	pbuf_free(p);

	return ERR_OK;

}

/*-----------------------------------------------------------------------------------*/
err_t
rfcomm_accept(void *arg, struct rfcomm_pcb *pcb, err_t err) 
{
  //struct ppp_pcb *ppppcb;
  //struct netif *netif;

  LWIP_DEBUGF(BT_IP_DEBUG, ("rfcomm_accept: CN = %d\n", rfcomm_cn(pcb)));
#ifdef DEBUG_HIGH
  printf("rfcomm_accept: CN = %d\n", rfcomm_cn(pcb));
#endif
  
  rfcomm_disc(pcb, rfcomm_disconnected);
  if(pcb->cn != 0) {
#ifdef DEBUG_MED
    printf(" rfcomm_accept: connected on a non-zero chanel... \n");
#endif

    // Get connection request okay from callback at external interface
    if (lib_threaded == 1) {
      if (lib_callbacks->rfcomm_connect_request != NULL) {
        if ((lib_callbacks->rfcomm_connect_request()) == 0) { 
          // Deny connection and restart library
        }
      } 
   }

    //rfcomm_recv(pcb, our_rfcomm_input);
#ifdef DEBUG_MED
    printf("  assigning obex_rfcomm_input to handle rfcomm data ... \n");
#endif
    rfcomm_recv(pcb, obex_rfcomm_input);

    global_pcb = pcb;

//    pcb->k = 6;
//printf("local credits = %d ???\n", pcb->k);

  }
  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/

